﻿using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;

namespace DBUtil;

/// <summary>
/// 表示数据库表名称(或者视图等同级对象)<br/>
/// 因为表名称可以有多种形式, 当含有特殊字符时, 在不同的场景处理的方式也不同, 所以封装了此类.<br />
/// 以mysql为例, 假设纯净表名为 【abc`d'ef】，则:
/// <code>
/// var tablePure = "abc`d'ef";
/// var tableMetaDataSeg = "'abc`d\'ef'";
/// var tableQuoted = "`abc``d'ef`";
/// 
/// //元数据查询时 使用 tableMetaDataSeg
/// var sql = 
///         $"""
///         select * from information_schema.columns t
///         where t.table_name = {tableMetaDataSeg}
///         """;
///  
/// //正常查询表数据时 使用 tableQuoted
/// var sql = $"select * from {tableQuoted}";
///         
/// //拼接sql时很少能用 tablePure, c#程序内部可能会用到, 如分表时:
/// db.Insert&lt;Person>().AsTable(tablePure => tablePure + "_20250101")
/// </code>
/// </summary>
public class TableName : ICloneable
{
    /// <summary>
    /// 如: 
    /// <code>
    /// var table = new TableName(
    ///                  name:"table1", 
    ///                  schemaName:"dbo", 
    ///                  dataBaseName:"testdb", 
    ///                  dblinkName:"dblink")
    /// </code>
    /// 也可以通过 <seealso cref="DBAccess.CreateTableName()"/> 先创建一个空的表名称, 然后再设置具体项, 如:
    /// <code>
    /// var table = db.CreateTableName();
    /// table.SetName("table1");
    /// table.SetSchemaName("dbo");
    /// table.SetDataBaseName("testdb");
    /// table.SetDBLinkName("dblink");
    /// //table.FullNameQuoted: [dblink].[testdb].[dbo].[table1]
    /// </code>
    /// </summary>
    public TableName(EnumDBType type, string name = null, string schemaName = null, string dataBaseName = null, string dblinkName = null)
    {
        this.type = type;
        this._name = name;
        this._schemaName = schemaName;
        this._dataBaseName = dataBaseName;
        this._dblinkName = dblinkName;

        switch (type)
        {
            case EnumDBType.MySql:
                _getFullNameQuoted = GetFullNameQuotedMySql;
                break;
            case EnumDBType.SqlServer:
                _getFullNameQuoted = GetFullNameQuotedSqlServer;
                break;
            case EnumDBType.Sqlite:
                _getFullNameQuoted = GetFullNameQuotedSqlite;
                break;
            case EnumDBType.PostgreSql:
                _getFullNameQuoted = GetFullNameQuotedPostgreSql;
                break;
            case EnumDBType.Oracle:
                _getFullNameQuoted = GetFullNameQuotedOracle;
                break;
            default:
                throw new NotSupportedException($"不支持的数据库类型: {type}");
        }
    }

    private string GetFullNameQuotedMySql(TableName table)
    {
        if (table == null || table.Name.IsNullOrWhiteSpace()) return null;
        if (table.SchemaName.IsNotNullOrWhiteSpace()) return $"`{table.SchemaName.Replace("`", "``")}`.`{table.Name.Replace("`", "``")}`";
        return $"`{table.Name.Replace("`", "``")}`";
    }
    private string GetFullNameQuotedSqlite(TableName table)
    {
        if (table == null || table.Name.IsNullOrWhiteSpace()) return null;
        if (table.SchemaName.IsNotNullOrWhiteSpace()) return $"\"{table.SchemaName.Replace("\"", "\"\"")}\".\"{table.Name.Replace("\"", "\"\"")}\"";
        return $"\"{table.Name.Replace("\"", "\"\"")}\"";
    }
    private string GetFullNameQuotedPostgreSql(TableName table) => GetFullNameQuotedSqlite(table);
    private string GetFullNameQuotedSqlServer(TableName table)
    {
        if (table == null || table.Name.IsNullOrWhiteSpace()) return null;
        var list = new List<string>();
        if (!string.IsNullOrWhiteSpace(table.DBLinkName)) list.Add("[" + table.DBLinkName.Replace("]", "]]") + "]");
        if (!string.IsNullOrWhiteSpace(table.DataBaseName)) list.Add("[" + table.DataBaseName.Replace("]", "]]") + "]");
        if (!string.IsNullOrWhiteSpace(table.SchemaName)) list.Add("[" + table.SchemaName.Replace("]", "]]") + "]");
        list.Add("[" + table.Name.Replace("]", "]]") + "]");
        return string.Join('.', list);
    }
    private string GetFullNameQuotedOracle(TableName table)
    {
        if (table == null || table.Name.IsNullOrWhiteSpace()) return null;
        var fullName = $"\"{table.Name}\"";
        if (!string.IsNullOrWhiteSpace(table.SchemaName)) fullName = $"\"{table.SchemaName}\"" + fullName;
        else if (!string.IsNullOrWhiteSpace(table.DataBaseName)) fullName = $"\"{table.DataBaseName}\"" + fullName;
        if (!string.IsNullOrWhiteSpace(table.DBLinkName)) fullName += "@" + table.DBLinkName;
        return fullName;
    }

    private readonly Func<TableName, string> _getFullNameQuoted = null;

    private readonly EnumDBType type;
    public EnumDBType Type => type;

    /// <summary>
    /// 远程db链接名称, 如下面的 <c>dblink</c>:
    /// <list type="bullet">
    /// <item>sqlserver: [dblink].[testdb].[dbo].[table1]</item>
    /// <item>oracle: user1.table1@dblink</item>
    /// </list>
    /// </summary>
    public string DBLinkName => _dblinkName;
    private string _dblinkName;
    /// <summary>
    /// 设置远程db链接名称
    /// </summary>
    public TableName SetDBLinkName(string dblinkName)
    {
        _dblinkName = dblinkName;
        _fullNameQuoted = null;
        return this;
    }

    /// <summary>
    /// 纯净数据库名称(前后没有引用符包裹, 也没有单引号包裹)
    /// </summary>    
    public string DataBaseName => _dataBaseName;
    private string _dataBaseName;
    /// <summary>
    /// 设置数据库名称
    /// </summary>
    public TableName SetDataBaseName(string dataBaseName)
    {
        if (type == EnumDBType.MySql)
        {
            _dataBaseName = _schemaName = dataBaseName;
        }
        else
        {
            _dataBaseName = dataBaseName;
        }
        _fullNameQuoted = null;
        return this;
    }

    /// <summary>
    /// 纯净模式名称(前后没有引用符包裹, 也没有单引号包裹)
    /// </summary>
    /// <inheritdoc cref="DataBaseName" path="/remarks"/>
    public string SchemaName => _schemaName;
    private string _schemaName;
    /// <summary>
    /// 设置模式名称
    /// </summary>
    public TableName SetSchemaName(string schemaName)
    {
        if (type == EnumDBType.MySql)
        {
            _dataBaseName = _schemaName = schemaName;
        }
        else
        {
            _dataBaseName = schemaName;
        }
        _fullNameQuoted = null;
        return this;
    }

    /// <summary>
    /// 纯净表名称(前后没有引用符包裹, 也没有单引号包裹)
    /// </summary>
    /// <inheritdoc cref="DataBaseName" path="/remarks"/>
    public string Name => _name;

    private string _name;
    /// <summary>
    /// 设置表名称
    /// </summary>
    public TableName SetName(string name)
    {
        _name = name;
        _fullNameQuoted = null;
        return this;
    }

    private string _fullNameQuoted;
    /// <summary>
    /// 全名称引用格式，如：
    /// <list type="number">
    /// <item>sqlserver：[testdb].[dbo].[table1]、[table1]</item>
    /// <item>mysql：`testdb`.`abcde`、`abcde`</item>
    /// <item>mysql含特殊字符：`testdb`.`abc``de`、`abc``de`</item>
    /// </list>
    /// </summary>
    /// <inheritdoc cref="TableName" path="/remarks"/>
    public string FullNameQuoted
    {
        get
        {
            _fullNameQuoted ??= _getFullNameQuoted(this);
            return _fullNameQuoted;
        }
    }

    /// <summary>
    /// 克隆一个新的对象
    /// </summary>
    public TableName CloneTableName()
    {
        return new TableName(type, dblinkName: _dblinkName, dataBaseName: _dataBaseName, schemaName: _schemaName, name: _name);
    }

    /// <summary>
    /// 克隆一个新的对象
    /// </summary>
    public object Clone() => CloneTableName();
}
